-module(vars).
%-compile([native]).
-export([
    conv_to_numeric/1,
    cast/2,
    common_numeric/2,
    bucket_new/0,
    bucket_add/3,
    bucket_get/2,
    test/0
    ]).

split_last_two([], [Dig]) -> {"0",[$0,Dig]};
split_last_two([], [Dig1,Dig2]) -> {"0",[Dig1,Dig2]};
split_last_two(Taken, [_Dig1,_Dig2]=Left) -> {Taken,Left};
split_last_two(Taken, [Dig|Left]) -> split_last_two(Taken++[Dig],Left).

conv_to_numeric({numbr, _}=Res) -> Res;
conv_to_numeric({numbar, _}=Res) -> Res;
conv_to_numeric({yarn, Val}) ->
    case catch list_to_integer(Val) of
        Numi when is_integer(Numi) -> {numbr, Numi};
        {'EXIT', {badarg, _Stack}} ->
            case list_to_float(Val) of
                Numf when is_float(Numf) -> {numbar, Numf}
            end
    end;
conv_to_numeric({troof, win}) -> {numbr, 1};
conv_to_numeric({troof, fail}) -> {numbr, 0}.

conv_to_numbr({numbr, _}=Res) -> Res;
conv_to_numbr({numbar, Val}) -> {numbr, round(Val)};
conv_to_numbr({yarn, Val}) ->
    case list_to_integer(Val) of
        Num when is_integer(Num) -> {numbr, Num}
    end;
conv_to_numbr({troof, win}) -> {numbr, 1};
conv_to_numbr({troof, fail}) -> {numbr, 0}.

conv_to_numbar({numbar, _}=Res) -> Res;
conv_to_numbar({numbr, Val}) -> {numbar, float(Val)};
conv_to_numbar({yarn, Val}) ->
    case list_to_float(Val) of
        Num when is_float(Num) -> {numbar, Num}
    end;
conv_to_numbar({troof, win}) -> {numbar, 1.0};
conv_to_numbar({troof, fail}) -> {numbar, 0.0}.

conv_to_string({yarn, _}=Res) -> Res;
conv_to_string({troof, win}) -> {yarn, "WIN"};
conv_to_string({troof, fail}) -> {yarn, "FAIL"};
conv_to_string({noob, noob}) -> {yarn, "NOOB"};
conv_to_string({numbr, Num}) -> {yarn, integer_to_list(Num)};
conv_to_string({numbar, Num}) ->
    {Int,Dec} = split_last_two([], integer_to_list(round(Num*100))),
    {yarn, Int ++ "." ++ Dec}.

conv_to_troof({troof, _}=Res) -> Res;
conv_to_troof({numbr, 0}) -> {troof, fail};
conv_to_troof({numbar, 0.0}) -> {troof, fail};
conv_to_troof({yarn, ""}) -> {troof, fail};
conv_to_troof(_) -> {troof, win}.

cast(Val, {type, yarn}) -> conv_to_string(Val);
cast(Val, {type, numbr}) -> conv_to_numbr(Val);
cast(Val, {type, numbar}) -> conv_to_numbar(Val);
cast(Val, {type, troof}) -> conv_to_troof(Val).

common_numeric(numbr, numbr) -> numbr;
common_numeric(numbar, numbr) -> numbar;
common_numeric(numbr, numbar) -> numbar;
common_numeric(numbar, numbar) -> numbar.

%%%

bucket_new() -> {bukkit, []}.

bucket_add({bukkit, Contents}, Key, {_,_}=Value) when is_list(Key) ->
    NonColiding = lists:filter(fun({A, _}) -> Key=/=A end, Contents),
    {bukkit, NonColiding ++ [{Key, Value}]}.

bucket_get({bukkit, []}, _) -> {noob, noob};
bucket_get({bukkit, Contents}, Key) when is_list(Key) ->
    case lists:filter(fun({A, _}) -> Key=:=A end, Contents) of
        [{Key, Value}] -> Value;
        [] -> {noob, noob}
    end.

%%%

test_set() -> [
    {fun(A) -> conv_to_string(A) end,
        {numbar, 2.34},
        {yarn, "2.34"}},
    {fun(A) -> conv_to_string(A) end,
        {numbar, 0.04},
        {yarn, "0.04"}},
    {fun({Val, Type}) -> cast(Val, Type) end,
        {{numbar, 0.0}, {type, troof}},
        {troof, fail}},
    {fun({Val, Type}) -> cast(Val, Type) end,
        {{yarn, "abc"}, {type, troof}},
        {troof, win}},
    {fun({Val, Type}) -> cast(Val, Type) end,
        {{yarn, "123"}, {type, numbr}},
        {numbr, 123}},
    {fun({Val, Type}) -> cast(Val, Type) end,
        {{yarn, "123.2"}, {type, numbar}},
        {numbar, 123.2}},
    {fun({Bucket, Key, Val}) -> bucket_add(Bucket, Key, Val) end,
        {bucket_new(), [{yarn, "abc"}], {numbr, 1}},
        {bukkit, [
            {[{yarn, "abc"}], {numbr, 1}}
            ]}},
    {fun({Bucket, Key, Val}) -> bucket_add(Bucket, Key, Val) end,
        {bucket_add(bucket_new(), [{yarn, "abc"}], {numbr, 1}), [{yarn, "abc"}], {troof, fail}},
        bucket_add(bucket_new(), [{yarn, "abc"}], {troof, fail})},
    {fun({Bucket, Key}) -> bucket_get(Bucket, Key) end,
        {bucket_add(bucket_new(), [{yarn, "abc"}], {numbr, 1}), [{yarn, "abc"}]},
        {numbr, 1}},
    {fun({Bucket, Key}) -> bucket_get(Bucket, Key) end,
        {bucket_add(bucket_new(), [{yarn, "abc"}], {numbr, 1}), [{yarn, "xyz"}]},
        {noob, noob}}
    ].

test() -> tester:test(test_set()).


